SCRIPT_STR = """\

<script type="text/ecmascript">
<![CDATA[

function init(evt) {
    window.status = "begin init";
    svgDoc = evt.target.ownerDocument;
    ID_INDEX = 0;
    COLOR_INDEX = 1;
    ATTR_INDEX = 2;
    SWITCH_COUNT_INDEX = 0;
    PER_ACT_INDEX = 1;
    END_ACT_INDEX = 2;
    PRIMARY_TITLE_INDEX = 3;
    SECONDARY_TITLE_INDEX = 4;
    PEER_INDEX = 5;
    NODE = 0;
    EDGE = 1;
    PREFIX = 2;
    TOP_LEFT = 0;
    TOP_RIGHT = 1;
    BOTTOM_LEFT = 2;
    BOTTOM_RIGHT = 3;
    X = 0;
    Y = 1;
    
    selected = [];
    edgeLookup = {};
    asLookup = {};
    
    //it makes more sense to just keep all of these in memory than query them each time we need to access them
    nodeRect = svgDoc.getElementById('nodeRect');
    nodeGradientRect = svgDoc.getElementById('nodeGradientRect');            
    nodeAsNum = svgDoc.getElementById("nodeAsNum");
    nodeAsOwner = svgDoc.getElementById("nodeAsOwner");
    nodeSecondaryText = svgDoc.getElementById("nodeSecondaryText");
    nodePrimaryText = svgDoc.getElementById("nodePrimaryText");
    nodeSwitchLabel = svgDoc.getElementById("nodeSwitchLabel");
    nodeSwitchCount = svgDoc.getElementById("nodeSwitchCount");
    nodeDisplayLine = svgDoc.getElementById("nodeDisplayLine");

    edgeRect = svgDoc.getElementById('edgeRect');
    edgeFromAsText = svgDoc.getElementById("edgeFromAsText");
    edgeFromAsNum = svgDoc.getElementById("edgeFromAsNum");
    edgeFromAsOwner = svgDoc.getElementById("edgeFromAsOwner");
    edgeToAsText = svgDoc.getElementById("edgeToAsText");    
    edgeToAsNum = svgDoc.getElementById("edgeToAsNum");
    edgeToAsOwner = svgDoc.getElementById("edgeToAsOwner");
    edgeDisplayLine = svgDoc.getElementById("edgeDisplayLine");
    
    displays = [svgDoc.getElementById('nodeDisplay'), svgDoc.getElementById('edgeDisplay'), svgDoc.getElementById('prefixDisplay')];
    percentGraphs = [svgDoc.getElementById("nodePercentGraph"), svgDoc.getElementById("edgePercentGraph")];
    percentActiveTexts = [svgDoc.getElementById("nodePercentActiveText"), svgDoc.getElementById("edgePercentActiveText")];
    timeElapsedTexts = [svgDoc.getElementById("nodeTimeElapsedText"), svgDoc.getElementById("edgeTimeElapsedText")];
    percentRects = [svgDoc.getElementById("nodePercentRect"), svgDoc.getElementById("edgePercentRect")];
    
    lastControl = document.getElementById("controlsShowAll");

    %s
    //Grab the translation of all the polygons in the svgs
    var graph0 = svgDoc.getElementById("graph0");
    var transform = graph0.getAttribute("transform");
    var translateIndex = transform.indexOf("translate");
    if (translateIndex !== -1) {
        var startIndex = translateIndex + 10;
        var xy = transform.substr(startIndex, transform.length - startIndex - 1).split(" ");
        TRANSLATE = [parseFloat(xy[0]), parseFloat(xy[1])];
    } else {
        TRANSLATE = [0, 0];
    }
    
    window.setTimeout("loadBBoxValues();",100);
    window.defaultStatus = "done init";
}

function loadBBoxValues() {
    for (var i = 0; i < displays.length; i++) {
        displays[i].setAttribute("visibility", "visible");
    }
    NODE_RECT_HEIGHT = displays[NODE].getBBox().height + PADDING;
    EDGE_RECT_HEIGHT = displays[EDGE].getBBox().height + PADDING;

    nodeRect.setAttribute("height", NODE_RECT_HEIGHT);
    edgeRect.setAttribute("height", EDGE_RECT_HEIGHT);
    var prefixRect = document.getElementById("prefixRect");
    
    var prefixBBox = displays[PREFIX].getBBox();
    PREFIX_RECT_WIDTH = prefixBBox.width + PADDING;
    PREFIX_RECT_HEIGHT = prefixBBox.height + PADDING;
    prefixRect.setAttribute("width", PREFIX_RECT_WIDTH);
    prefixRect.setAttribute("height", PREFIX_RECT_HEIGHT);
    for (var i = 0; i < displays.length; i++) {
        displays[i].setAttribute("visibility", "hidden");
    }
}

function toggleNodes(type) {    
    if (type == "all") {
        curControl = document.getElementById("controlsShowAll");
    } else if (type == "end") {
        curControl = document.getElementById("controlsShowEndActive");
    } else {
        curControl = document.getElementById("controlsShowPercent");
    }
    curControl.setAttribute("fill", "#0000FF");
    curControl.setAttribute("text-decoration", "underline");
    lastControl.setAttribute("fill", "#000000");
    lastControl.setAttribute("text-decoration", "none");
    for (var node in asLookup) {
        if ((type == "end" && asLookup[node][END_ACT_INDEX] == 0) ||
            (type == "percent" && asLookup[node][PER_ACT_INDEX] < .05)) {
            document.getElementById(node).setAttribute("visibility", "hidden");
        } else {
            document.getElementById(node).setAttribute("visibility", "visible");
        }
    }
    for (var edge in edgeLookup) {
        if ((type == "end" && edgeLookup[edge][END_ACT_INDEX] == 0) ||
            (type == "percent" && edgeLookup[edge][PER_ACT_INDEX] < .05)) {
            document.getElementById(edge).setAttribute("visibility", "hidden");
        } else {
            document.getElementById(edge).setAttribute("visibility", "visible");
        }
    }
    lastControl = curControl;
}

function parsePoints(pointStr) {
    var pointsArray = pointStr.split(" ");
    var min = [];
    var max = [];
    min[X] = 10000000;
    min[Y] = 10000000;
    max[X] = -10000000;
    max[Y] = -10000000;
    for (var i = 0; i < pointsArray.length; i++) {
        var xy = pointsArray[i].split(",");
        var x = parseInt(xy[0], 10);
        var y = parseInt(xy[1], 10);
        if (x < min[X]) {
            min[X] = x;
        }
        if (x > max[X]) {
            max[X] = x;
        }
        if (y < min[Y]) {
            min[Y] = y;
        }
        if (y > max[Y]) {
            max[Y] = y;
        }
    }
    
    var points = [];
    for (var j = 0; j < 4; j++) {
        points[j] = [];
    }
    points[TOP_LEFT][X] = min[X];
    points[TOP_LEFT][Y] = min[Y];
    points[TOP_RIGHT][X] = max[X];
    points[TOP_RIGHT][Y] = min[Y];
    points[BOTTOM_LEFT][X] = min[X];
    points[BOTTOM_LEFT][Y] = max[Y];
    points[BOTTOM_RIGHT][X] = max[X];
    points[BOTTOM_RIGHT][Y] = max[Y];
    return points;
}

function uncolorObj() {
    for (var i = 0; i < selected.length; i++) {
        colorState = selected[i];
        var ele = svgDoc.getElementById(colorState[ID_INDEX]);
        ele.setAttribute(colorState[ATTR_INDEX], colorState[COLOR_INDEX]);
    }
    selected = [];
}

function colorObj(id, attr) {
    var objToColor = svgDoc.getElementById(id);
    if (objToColor === null) {
        window.status = "NULL OBJECT ID " + id;
        return;
    }

    var colorState = [];
    colorState[ID_INDEX] = id;
    colorState[COLOR_INDEX] = objToColor.getAttribute(attr); //store the previous color
    colorState[ATTR_INDEX] = attr; //the attribute we've colored'
    selected.push(colorState);

    objToColor.setAttribute(attr, "yellow");
    //objToColor.parentNode.parentNode.appendChild(objToColor); //equivalent to "bring to front" since this replaces the existing node
}

function moveToTop(svgNode)
{
   svgNode.parentNode.appendChild( svgNode );
}

function setGradient(switchcount, gradWidth, isPeer) {
    percent = Math.min(5.0, (Math.log(switchcount+1) / Math.log(10)))/5.0; //This gives us the percentage along the gradient for triangle/label
    
    curY = nodeSwitchLabel.getAttribute("transform").split(" ")[1]; //note: this has end paranthesis attached
    triX = gradWidth * percent + PADDING;
    nodeSwitchCount.firstChild.nodeValue = switchcount;
    nodeSwitchLabel.setAttribute("transform", "translate(" + triX + " " + curY);
    
    if (isPeer) {
        nodeGradientRect.setAttribute("fill", "url(#peer_gradient)");
        nodeRect.setAttribute("stroke", "#FF5A00");
    } else {
        nodeGradientRect.setAttribute("fill", "url(#as_gradient)");
        nodeRect.setAttribute("stroke", "#0033CC");
    }
    
    nodeGradientRect.setAttribute("width", gradWidth);
}

function setPercentActiveText(percentActive, mouseOverWidth, type) {
    rectX = parseFloat(percentRects[type].getAttribute("x")); //the current x value of the percent active rectangle
    percentRectWidth = mouseOverWidth - PADDING * 3 - timeElapsedTexts[type].getBBox().width - rectX; //the width the percent active rectangle should have
    labelX = percentRectWidth * percentActive + rectX; //the x value of the vertical line/percent label 
    labelY = percentGraphs[type].getAttribute("transform").split(" ")[1]; //note: this has paranthesis attached
    
    percentGraphs[type].setAttribute("transform", "translate(" + labelX + " " + labelY);
    percentActiveTexts[type].firstChild.nodeValue = (percentActive*100).toFixed(1) + "%%";
    timeElapsedTexts[type].setAttribute("x", mouseOverWidth - PADDING);
    percentRects[type].setAttribute("width", percentRectWidth);
}

function placeMouseOverBox(width, height, points, type) {
    var nodeWidth = points[TOP_RIGHT] - points[TOP_LEFT];
    var nodeHeight = points[BOTTOM_RIGHT] - points[TOP_RIGHT];
    var x = (points[TOP_LEFT][X] + points[TOP_RIGHT][X]) / 2 + TRANSLATE[X] - width / 2;
    var y = points[TOP_LEFT][Y] + TRANSLATE[Y] - height - PADDING;
    
    window.status = "Top Left Point: " + points[TOP_LEFT] + " TRANSLATE[Y]: " + TRANSLATE[Y];// + " height: " + height;
    if (y < 0) { //if the display goes over the top of the graph, move it to the left of the node
        y = 2;
        x = points[TOP_LEFT][X] + TRANSLATE[X] - width - PADDING;
        if (x < 0) {
            x = points[TOP_RIGHT] + PADDING;
        }
    } else {
        if (x < PADDING) {
            x = PADDING;
        } else if (x + width > GRAPH_SIZE[X]) {
            x -= (x + width + PADDING - GRAPH_SIZE[X]);
        }
    }
    
    //window.status = "x: " + x + " y: " + y + " width: " + width + " height: " + height;
    if (type == NODE) {
        nodeDisplayLine.setAttribute("x2", width - PADDING);
        nodeRect.setAttribute("width", width);
    }
    window.status = "Moving display: " + displays[type] + " to " + x + ", " + y;
    displays[type].setAttribute("transform", "translate(" + x + " " + y + ")");
}

function displayMouseOverNode(evt, id) {   
    var stats = asLookup[id];
    var peer = (stats[PEER_INDEX] == 1);
    
    nodeAsNum.firstChild.nodeValue = "AS" + id;
    nodeAsOwner.firstChild.nodeValue = "(" + stats[PRIMARY_TITLE_INDEX] + ")";
    nodeSecondaryText.firstChild.nodeValue = stats[SECONDARY_TITLE_INDEX];    

    var width = Math.max(Math.max(nodePrimaryText.getBBox().width, nodeSecondaryText.getBBox().width), MIN_MOUSE_OVER_WIDTH) + PADDING * 2;
    var points = parsePoints(svgDoc.getElementById(id + "polygon").getAttribute("points"));

    setGradient(stats[SWITCH_COUNT_INDEX], width - PADDING * 2, peer);
    setPercentActiveText(stats[PER_ACT_INDEX], width, NODE);
    placeMouseOverBox(width, NODE_RECT_HEIGHT, points, NODE);
}

function displayMouseOverEdge(evt, fromAs, toAs, id) {
    fromState = asLookup[fromAs];
    toState = asLookup[toAs];
    edgeState = edgeLookup[id];

    edgeToAsNum.firstChild.nodeValue = "AS" + toAs;
    edgeToAsOwner.firstChild.nodeValue = " (" + toState[PRIMARY_TITLE_INDEX] + ")";
    edgeFromAsNum.firstChild.nodeValue = "AS" + fromAs;
    edgeFromAsOwner.firstChild.nodeValue = " (" + fromState[PRIMARY_TITLE_INDEX] + ")";        
    
    var width = Math.max(Math.max(edgeFromAsText.getBBox().width, edgeToAsText.getBBox().width), MIN_MOUSE_OVER_WIDTH) + PADDING * 2;
    var x = evt.clientX * SCALE + PADDING;
    var y = evt.clientY * SCALE + PADDING;
    
    window.status = "evt.clientX: " + evt.clientX + " evt.clientY: " + evt.clientY + " SCALE: " + SCALE + " x: " + x + " y: " + y;
    if (x < 0) {
        x = 0;
    } else if (x + width > GRAPH_SIZE[X]) {
        x -= (x + width + PADDING - GRAPH_SIZE[X]);
    }
    if (y + EDGE_RECT_HEIGHT > GRAPH_SIZE[Y]) {
        y -= (y + EDGE_RECT_HEIGHT + PADDING - GRAPH_SIZE[Y]);
    }
    
    setPercentActiveText(edgeState[PER_ACT_INDEX], width, EDGE);
    edgeDisplayLine.setAttribute("x2", width - PADDING);    
    edgeRect.setAttribute("width", width);
    var hexColor = (Math.floor(128 - 128*edgeState[PER_ACT_INDEX])).toString(16);
    if (hexColor.length == 1) {
        hexColor = "0" + hexColor;
    }
    edgeRect.setAttribute("stroke", "#" + hexColor + hexColor + hexColor);
    displays[EDGE].setAttribute("transform", "translate(" + x + ", " + y + ")");
}

function mouseOverNode(evt, id) {
    window.status = "(start) Mouse Over Node " + id;
    displays[NODE].setAttribute("visibility", "visible");    
    uncolorObj();
    colorObj(id + "polygon", "stroke");
    displayMouseOverNode(evt, id);
    window.status = "(end) Mouse Over Node " + id;
}

function mouseOverEdge(evt, id) {
    window.status = "(start) Mouse Over Edge " + id;
    displays[EDGE].setAttribute("visibility", "visible");
    idSplit = id.split("to");
    fromAs = parseInt(idSplit[0]);
    toAs = parseInt(idSplit[1]);
    
    uncolorObj(); //in case anything was missed, mouseOut seems to fail to fire sometimes
    colorObj(id + "path", "stroke"); //the path
    colorObj(id + "polygon", "stroke"); //the triangle edge
    colorObj(id + "polygon", "fill"); //the triangle
    colorObj(fromAs + "polygon", "stroke");
    if (fromAs != toAs) {
        colorObj(toAs + "polygon", "stroke");
    }
    displayMouseOverEdge(evt, fromAs, toAs, id);
    window.status = "(end) Mouse Over Edge " + id;
}

function mouseOverPrefix(evt, id) {
    window.status = "Over prefix. Display: " + displays[PREFIX];
    displays[PREFIX].setAttribute("visibility", "visible");    
    placeMouseOverBox(PREFIX_RECT_WIDTH, PREFIX_RECT_HEIGHT, parsePoints(svgDoc.getElementById(id + "polygon").getAttribute("points")), PREFIX);
}

function mouseOutNode(evt, id) {
    window.status = "(start) Mouse Out node " + id;
    displays[NODE].setAttribute("visibility", "hidden");
    uncolorObj();
    window.status = "(end) Mouse Out node " + id;
}

function mouseOutEdge(evt, id) {
    window.status = "(start) Mouse Out Edge " + id;
    displays[EDGE].setAttribute("visibility", "hidden");
    uncolorObj();
    window.status = "(end) Mouse Out Edge " + id;
}

function mouseOutPrefix(evt) {
    displays[PREFIX].setAttribute("visibility", "hidden");
}

function nodeClick(evt, id) {
}
]]>
</script>

"""

class ScriptGen:
    def __init__(self, stateDbHelper, maxWidth):
        self.dbHelper = stateDbHelper
        self.maxWidth = maxWidth
        self.prefixId = None
        
    def getScriptStr(self, prefixId, graphWidth, graphHeight):
        initStr = ''
        initStr += "\tGRAPH_SIZE = [%.2f, %.2f];\n" % (graphWidth, graphHeight)
        initStr += "\tMIN_MOUSE_OVER_WIDTH = %.2f;\n" % (graphWidth/5)
        initStr += "\tSCALE = %.3f;\n" % (max(1, float(graphWidth)/self.maxWidth))
        initStr += "\tPADDING = %s;\n" % (7 * max(1, graphWidth/self.maxWidth))
        
        peerStates = self.dbHelper.query("SELECT peer_states.as_num, primary_title, secondary_title, peer_states.per_act, \
        peer_states.switch_count, peer_states.act_count FROM as_lookup RIGHT JOIN peer_states ON peer_states.as_num = as_lookup.as_num \
        WHERE prefix_id=%s ORDER BY peer_states.as_num;" % prefixId)
        
        peers = {}
        for state in peerStates:
            peers[state[0]] = True
            end_act = state[5] > 0 and 1 or 0
            initStr += "\tasLookup[%s] = [%s, %.3f, %s, '%s', '%s', 1];\n" % (state[0], state[4], state[3], end_act, state[1], state[2])
        
        nodeStates = self.dbHelper.query("SELECT node_states.as_num, primary_title, secondary_title, node_states.per_act, \
        node_states.switch_count, node_states.act_count FROM as_lookup RIGHT JOIN node_states ON node_states.as_num = as_lookup.as_num \
        WHERE prefix_id=%s ORDER BY node_states.as_num;" % prefixId)
        
        for state in nodeStates:
            if state[0] not in peers:
                end_act = state[5] > 0 and 1 or 0
                initStr += "\tasLookup[%s] = [%s, %.3f, %s, '%s', '%s', 0];\n" % (state[0], state[4], state[3], end_act, state[1], state[2])
        
        edgeStates = self.dbHelper.query("SELECT as_num, as_parent, switch_count, per_act, act_count FROM parent_states WHERE prefix_id=%s \
        ORDER BY as_num ASC, as_parent ASC;" % prefixId)
        
        for state in edgeStates:
            end_act = state[4] > 0 and 1 or 0
            initStr += '\tedgeLookup["%sto%s"] = [%s, %.3f, %s];\n' % (state[0], state[1], state[2], state[3], end_act)
            
        return SCRIPT_STR % initStr